import { RouteLocationNormalized } from "vue-router";
import Views from "../types/views";
import Calc from "./calc";
import { getPopupStr, parseStr } from "./tools";
import { hasOwn, isFunction, isObject, isString } from "./valid";
import { nextTick } from "vue";
import { DirectionStr, OptionType } from "../types/focus";
import Creater from "../types/creater";
import Pools from "../types/pools";

export default class MoveRule extends Calc {
  private _views: Views = new Views(); // 视图区域
  private _path: string[] = []; // 路由匹配路径列表
  private selectName: string = 'selected';
  private focusName: string = 'focushover';
  private popSignList: string[] = [];

  protected constructor(options?: OptionType) {
    super(options)
    this.selectName = options?.selectName || "selected"
    this.focusName = options?.focusName || "focushover"
  }

  get pointer(): Creater | undefined {
    let pointer: Creater | undefined
    this.ergodicViews((view: Views) => {
      let layer = view.getLayer(this.popSign)
      if (layer && layer.pointer) {
        pointer = layer.pointer
      }
      return !!pointer
    })
    return pointer
  }

  get popSign(): string {
    return this.popSignList.length ? this.popSignList[0] : ""
  }

  /**
   * 获取焦点所属Views
   * @param routeDepath 节点路由深度
   * @param routeVpath 节点路由路径
   * @returns
   */
  protected getView(routeDepath: number, routeVpath: string): Views {
    if (routeDepath === 0) {
      return this._views;
    } else {
      let list = this._path;
      let pathArr = routeVpath.split(':::').filter((item) => item !== 'public');
      let views = this._views;
      for (let i = 0; i < routeDepath; i++) {
        views = views.routeViews[list[i]][pathArr[i]];
      }
      return views;
    }
  }

  /**
   * 根据路由创建视图层，并变更当前路径对象
   * @param {RouteLocationNormalized} to VUE路由守卫中的目标
   */
  get createView() {
    return (to: RouteLocationNormalized) => {
      // 未匹配到路由信息，不创建视图层
      if (!to.matched.length) {
        return false;
      }
      let parentViews: { [key: string]: Views } = { default: this._views };
      let tempPath: string[] = [];
      // 根据匹配到的路由信息创建视图层
      to.matched.forEach((value) => {
        let name = parseStr(value.name);
        if (name) {
          if (value.components) {
            let parentRouteViews: { [key: string]: { [key: string]: Views } } = parentViews['default'].routeViews;
            for (let propName of Object.keys(value.components)) {
              parentRouteViews = parentViews[hasOwn(parentViews, propName) ? propName : 'default'].routeViews;
              if (!hasOwn(parentRouteViews, name)) {
                parentRouteViews[name] = {};
              }
              if (!hasOwn(parentRouteViews[name], propName)) {
                parentRouteViews[name][propName] = new Views(parentViews[hasOwn(parentViews, propName) ? propName : 'default']);
              }
              parentViews[hasOwn(parentViews, propName) ? propName : 'default'].viewSign = name;
            }
            parentViews = parentRouteViews[name];
          } else {
            let parentRouteViews: { [key: string]: { [key: string]: Views } } = parentViews['default'].routeViews;
            if (!hasOwn(parentRouteViews, name)) {
              parentRouteViews[name] = {};
            }
            if (!hasOwn(parentRouteViews[name], 'default')) {
              parentRouteViews[name]['default'] = new Views(parentViews['default']);
            }
            parentViews['default'].viewSign = name;
            parentViews = parentRouteViews[name];
          }
          tempPath.push(name);
        } else {
          throw new Error(`路由：${value.path}，未指定name属性`);
        }
      });
      this._path = tempPath;
    }
  }

  /**
   * 获取指定popup弹层基础焦点列表
   * @param createrOrPopup 焦点对象或弹层标记
   * @returns Creater[]
   */
  private getList(createrOrPopup?: Creater | string): Creater[] {
    if (createrOrPopup instanceof Creater) {
      const creater = createrOrPopup
      return creater.__list__ || []
    } else if (typeof createrOrPopup === "string") {
      const popup = createrOrPopup
      let list: Creater[] = []
      // 获取指定弹层或当前页面焦点列表
      this.ergodicViews((value) => {
        let layer = value.getLayer(popup)
        if (layer) {
          list = layer.items
          return true
        }
        return false
      })
      return list
    }
    return []
  }

  /**
   * 按当前路径遍历Views对象
   * @param func 视图匹配方法
   * @param views 基础视图
   * @returns
   */
  private ergodicViews(func: (prop: Views) => boolean, views: Views = this._views) {
    let flag = func(views)
    if (!flag) {
      let parentViewsArr: Views[] = [views]
      for (let index = 0; index < this._path.length; index++) {
        let prop = this._path[index]
        let arr: Views[] = []
        parentViewsArr.forEach((value: Views) => {
          if (hasOwn(value.routeViews, prop)) {
            arr.push(...Object.values(value.routeViews[prop]))
          }
        })
        parentViewsArr = arr
        for (let item = 0; item < arr.length; item++) {
          if (func(arr[item])) {
            flag = true
            return flag
          }
        }
      }
    }
    return flag
  }

  /**
   * 打开新的弹层
   * @param popup 弹层标记
   * @param callBack 切换弹层后的会调，可能未获焦
   */
  get openPop() {
    return (popup?: string, callBack?: () => void) => {
      let flag = true
      const popSignStr = getPopupStr(popup)
      this.ergodicViews((view: Views) => {
        view.popSign = ''
        if (popup !== undefined && flag && view.getLayer(popup)) {
          view.popSign = popup
          flag = false
        }
        return !flag;
      })
      popup ? !flag && getPopupStr(this.popSign) !== popSignStr && this.popSignList.unshift(popup) : (this.popSignList = [])
      callBack && isFunction(callBack) ? callBack() : !this.pointer && this.initFocus(0, 0)
    }
  }

  /**
   * 关闭弹窗
   */
  get closePop() {
    return () => {
      this.popSignList.shift()
      this.openPop(this.popSignList[0])
    }
  }

  /**
   * 设置焦点
   * @param next 焦点对象
   * @returns Creater | undefined
   */
  get checkFocus() {
    return (next: Creater | undefined): Creater | undefined => {
      let prev: Creater | undefined = this.pointer
      if (next !== undefined && next !== prev) {
        if (prev !== undefined) {
          prev.remove(this.focusName)
          prev.__pools__?.setPointer()
          if (typeof prev.blur === "function") {
            prev.blur(prev)
          }
        }
        if (next.sign) {
          if (prev && next.sign === prev.sign) {
            next.__sign__?.forEach((value) => {
              value.selected = value === next
              // 添加移除选中类名
              value.selected ? value.add(this.selectName) : value.remove(this.selectName)
            })
          } else {
            next = this.getSign(next)
          }
        }
        next?.add(this.focusName)
        next?.__pools__?.setPointer(next)
        if (typeof next?.focus === "function") {
          next?.focus(next)
        }
        return next
      }
    }
  }

  /**
   * 焦点移动方法
   * @param direction 移动方向（left,right,up,down）
   * @param pointer 焦点对象
   * @returns Creater | undefined
   */
  get moveFocus() {
    return (direction: DirectionStr, pointer: Creater | undefined = this.pointer): Creater | undefined => {
      // 判断当前有焦点时再移动
      return pointer ? this.checkFocus(this.rulesInArea(direction, pointer, false, this.popSign)) : this.checkPosition(0, 0)
    }
  }

  /**
   * 根据位置信息设置焦点
   * @param left 元素X方向偏移量
   * @param top 元素Y方向偏移量
   * @param callBack 设置焦点后需要执行的方法(非必传)
   * @returns Creater | undefined
   */
  get checkPosition() {
    return (left: number, top: number, callBack?: (prop?: Creater) => void): Creater | undefined => {
      let tempArr: Creater[] = this.getList(this.popSign)
      let temp: Creater | undefined = this.rulesPosition(left, top, tempArr, this.popSign)
      if (temp && temp.sign) {
        temp = this.getSign(temp)
      }
      this.checkFocus(temp)
      callBack && isFunction(callBack) && callBack(this.pointer)
      return this.pointer
    }
  }

  /**
   * 自定义条件初始化焦点
   * @param check 焦点筛选条件方法，并返回Boolean值
   * @param callBack 非必传字段、初始化焦点后需要执行的方法
   */
  get createFocus() {
    return (check: (prop: Creater) => boolean, callBack?: (prop?: Creater) => void) => {
      let tempArr = this.getList(this.popSign)
      let temp: Creater | undefined = this.ergodicList(tempArr, this.popSign, check)
      if (temp?.sign) {
        temp = this.getSign(temp)
      }
      this.checkFocus(temp)
      callBack && isFunction(callBack) && callBack(this.pointer)
    }
  }

  /**
   * 通用焦点初始化方法
   * @param a 焦点X方向偏移量/焦点筛选条件方法
   * @param b 焦点Y方向偏移量/焦点初始化后的回调
   * @param c 焦点初始化后的回调
   */
  get initFocus() {
    return (a: number | ((prop: Creater) => boolean) | Creater, b?: number | ((prop?: Creater) => void), c?: (prop?: Creater) => void) => {
      nextTick(() => {
        if (typeof a === "function") {
          this.createFocus(a, b && typeof b === "function" ? b : undefined)
        } else if (typeof a === "number" && typeof b === "number") {
          this.checkPosition(a, b, c)
        } else if (a instanceof Creater) {
          if (a.name === 'items') {
            this.checkFocus(a)
            b && typeof b === "function" && b(a)
          }
        }
      })
    }
  }

  /**
   * 下一个焦点，仅处理当前弹层水平方向的下一个
   * @param flag 是否在限定区域内移动
   * @param callBack 触及边界且换行成功后执行的方法
   * @returns Creater | undefined
   */
  get next() {
    return (flag: boolean, callBack: (prop?: Creater) => void): Creater | undefined => {
      let pointer: Creater | undefined
      if (this.pointer) {
        pointer = this.rulesInArea('right', this.pointer, flag, this.popSign)
        if (pointer) {
          // 未到边界继续执行向右的逻辑
          this.checkFocus(pointer)
        } else {
          // 移动到边界后计算下一行第一个元素
          pointer = this.rulesTurnDown(this.pointer, this.popSign, flag)
          pointer && this.checkFocus(pointer) && isFunction(callBack) && callBack(this.pointer)
        }
      } else {
        this.checkPosition(0, 0)
        flag && this.pointer && isFunction(callBack) && callBack(this.pointer)
      }
      return pointer
    }
  }

  /**
   * 上一个焦点，仅处理当前弹层水平方向的上一个
   * @param flag 是否在限定区域内移动
   * @param callBack 触及边界且换行成功后执行的方法
   * @returns Creater | undefined
   */
  get prev() {
    return (flag: boolean, callBack: () => void): Creater | undefined => {
      let prev: Creater | undefined = this.pointer
      let next: Creater | undefined
      if (prev) {
        next = this.rulesInArea('left', prev, flag, this.popSign)
        if (next) {
          // 未到边界继续执行向右的逻辑
          this.checkFocus(next)
        } else {
          // 移动到边界后计算上一行最后一个元素
          next = this.rulesTurnUp(prev, this.popSign, flag)
          next && this.checkFocus(next) && isFunction(callBack) && callBack()
        }
      } else {
        next = this.checkPosition(0, 0)
        flag && next && isFunction(callBack) && callBack()
      }
      return next
    }
  }

  /**
   * 移动到当前焦点所在行的第一个|移动标记到焦点所在行的第一个
   * @param createrOrFlag 是否限定区域|含有sign标记的焦点对象
   * @returns Creater | undefined
   */
  get start() {
    return (createrOrFlag: Creater | boolean): Creater | undefined => {
      if (isObject(createrOrFlag)) {
        const creater = createrOrFlag as Creater;
        if (creater.sign) {
          let next: Creater | undefined = creater
          let prev: Creater | undefined = creater
          while ((prev = this.rulesInSign('left', prev))) {
            next = prev
          }
          if (next !== creater) {
            creater.remove(this.selectName)
            next.add(this.selectName)
          }
          return next
        } else {
          console.error(`您当前选择的焦点对象，未含有sign标记`)
        }
      } else {
        if (this.pointer) {
          const flag = createrOrFlag as boolean
          let target: Creater | undefined = this.pointer
          let tempObj: Creater | undefined
          // 仅在焦点标记的区域、或默认全局移动到左侧边界
          while ((tempObj = this.rulesInArea('left', target, flag, this.popSign))) {
            target = tempObj
          }
          // 移动到不同标记的焦点时，自动切到对应选中节点上
          if (target.sign !== this.pointer.sign) {
            target = this.getSign(target.sign, target.popup)
          }
          return this.checkFocus(target)
        } else {
          return this.checkPosition(0, 0)
        }
      }
    }
  }

  /**
   * 移动到当前焦点所在行的最后一个|移动标记到焦点所在行的最后一个
   * @param createrOrFlag 是否限定区域|含有sign标记的焦点对象
   * @returns Creater | undefined
   */
  get end() {
    return (createrOrFlag: boolean | Creater): Creater | undefined => {
      if (isObject(createrOrFlag)) {
        let creater = createrOrFlag as Creater;
        if (creater.sign) {
          let next: Creater | undefined = creater
          let prev: Creater | undefined = creater
          while ((prev = this.rulesInSign('right', prev))) {
            next = prev
          }
          if (next !== creater) {
            creater.remove(this.selectName)
            next.add(this.selectName)
          }
          return next
        } else {
          console.error(`您当前选择的焦点对象，未含有sign标记`)
        }
      } else {
        if (this.pointer) {
          let flag = createrOrFlag as boolean
          let target: Creater | undefined = this.pointer
          let tempObj: Creater | undefined
          // 仅在焦点标记的区域、或默认全局移动到左侧边界
          while ((tempObj = this.rulesInArea('right', target, flag, this.popSign))) {
            target = tempObj
          }
          // 移动到不同标记的焦点时，自动切到对应选中节点上
          if (target.sign !== this.pointer.sign) {
            target = this.getSign(target.sign, target.popup)
          }
          return this.checkFocus(target)
        } else {
          return this.checkPosition(0, 0)
        }
      }
    }
  }

  /**
   * 清除当前层级焦点内容
   */
  get clearFocus() {
    return () => {
      let popup = this.popSign
      this.ergodicViews((viewItem: Views) => {
        let layer = viewItem.getLayer(popup)
        if (layer) {
          let { pointer } = layer
          if (pointer) {
            pointer.remove(this.focusName)
            pointer.blur && isFunction(pointer.blur) && pointer.blur(pointer)
          }
          layer.setPointer()
        }
        return false
      })
    }
  }

  /**
   * 处理同一标记类型，当前选中状态
   * @param sign 元素标记名称
   * @param fn 返回布尔类型来标记当前焦点选中或未选中
   * @param popup 节点所在层级标记（非必传）
   */
  get checkSign() {
    return (sign: string, fn: (prop: Creater) => boolean, popup?: string) => {
      let tempArr = this.getSignList(sign, popup)
      tempArr.forEach((createrItem: Creater) => {
        createrItem.selected = fn(createrItem)
        // 添加移除选中类名
        createrItem.selected
          ? createrItem.add(this.selectName)
          : createrItem.remove(this.selectName)
      })
    }
  }

  /**
   * 根据弹层和标签标记名称获取标记列表
   * @param createrOrSign 分组标记名称
   * @param popup 弹层标记名称
   * @returns Creater[]
   */
  get getSignList() {
    return (createrOrSign: string | Creater, popup?: string): Creater[] => {
      let list: Creater[] = []
      if (isObject(createrOrSign)) {
        let creater = createrOrSign as Creater;
        list = creater.__sign__ || []
      } else if (isString(createrOrSign)) {
        let sign = createrOrSign as string;
        this.ergodicViews((viewItem: Views) => {
          let pool: Pools | null = viewItem.getLayer(popup)
          if (pool !== null) {
            let temp = pool.getSignList(sign)
            if (temp) {
              list = temp;
              return true;
            }
          }
          return false;
        })
      }
      return list
    }
  }

  /**
   * 获取指定标记类型选中状态的数据内容
   * @param createrOrSign 元素标记名称|焦点元素
   * @param popup 节点所在层级标记（非必传）
   * @returns Creater | undefined
   */
  get getSign() {
    return (createrOrSign: string | Creater, popup?: string): Creater | undefined => {
      let data: Creater | undefined;
      if (createrOrSign instanceof Creater) {
        let creater = createrOrSign;
        creater.__sign__?.forEach((createrItem: Creater) => {
          createrItem.selected && (data = createrItem);
        })
        if (!data) {
          creater.add(this.selectName)
          creater.selected = true
          data = creater
        }
      } else if (typeof createrOrSign === "string") {
        let sign = createrOrSign;
        let tempArr = this.getSignList(sign, popup)
        tempArr.forEach((createrItem: Creater) => {
          createrItem.selected && (data = createrItem)
        })
        !data && console.warn(`您查看的标记为${createrOrSign}的列表中，未查找到已选中元素`)
      }
      return data
    }
  }

  /**
   * 下一个标记移动方法
   * @param sign 标记名称
   * @param direction 方向（默认为向右移动）
   * @param popup 弹窗标记，非必传
   * @returns Creater | undefined
   */
  get nextSign() {
    return (sign: string, direction: DirectionStr = 'right', popup?: string): Creater | undefined => {
      let prev: Creater | undefined = this.getSign(sign, popup)
      let next: Creater | undefined = this.rulesInSign(direction, prev!)
      if (next !== undefined) {
        prev?.remove(this.selectName)
        prev?.__pools__?.setPointer()
        next.add(this.selectName)
        next.__pools__?.setPointer(next)
      }
      return next
    }
  }

  /**
   * 上一个标记移动方法
   * @param sign 标记名称
   * @param direction 方向（默认为向左移动）
   * @param popup 弹窗标记，非必传
   * @returns Creater | undefined
   */
  get prevSign() {
    return (sign: string, direction: DirectionStr = 'left', popup?: string): Creater | undefined => {
      let prev: Creater | undefined = this.getSign(sign, popup)
      let next: Creater | undefined = prev !== undefined ? this.rulesInSign(direction, prev) : undefined
      if (next) {
        prev?.remove(this.selectName)
        prev?.__pools__?.setPointer()
        next.add(this.selectName)
        next.__pools__?.setPointer(next)
      }
      return next
    }
  }
}
